package com.lvhr.rpc.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import com.lvhr.rpc.serializer.Serializer;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SPI 加载器（支持键值对映射）
 */
@Slf4j
public class SpiLoader {
    // 使用分段锁设计
    private static final Map<Class<?>, Map<String, Class<?>>> SERVICE_REGISTRY = new ConcurrentHashMap<>();
    private static final Map<String, Object> INSTANCE_CACHE = new ConcurrentHashMap<>();
    private static final String[] SPI_PATHS = {
            "META-INF/rpc/system/",
            "META-INF/rpc/custom/"
    };

    // 添加预加载检查机制
    private static volatile boolean loaded = false;

    /**
     * 系统启动时预加载指定接口的SPI配置
     */
    public static synchronized void loadAll() {
        if (loaded) return;
        Arrays.asList(Serializer.class).forEach(SpiLoader::load);
        loaded = true;
    }

    /**
     * 获取某个接口的实例（检查并加载所需接口配置，从注册表获取实现类映射，使用computeIfAbsent原子性地创建/获取实例）
     * @param serviceClass
     * @param key
     * @param <T>
     * @return
     */
    public static <T> T getInstance(Class<T> serviceClass, String key) {
        loadIfNeeded(serviceClass);
        Map<String, Class<?>> implMap = SERVICE_REGISTRY.get(serviceClass);
        if (implMap == null) {
            throw new RuntimeException(String.format("SpiLoader 未加载 %s 类型", serviceClass.getName()));
        }
        if (!implMap.containsKey(key)) {
            throw new RuntimeException(String.format("SpiLoader 的 %s 不存在 key=%s 的类型", serviceClass.getName(), key));
        }
        log.info("getInstance().implMap : {}",implMap.toString());
        return (T) INSTANCE_CACHE.computeIfAbsent(getCacheKey(serviceClass, key), k -> {
            Class<?> implClass = Optional.ofNullable(implMap.get(key))
                    .orElseThrow(() -> new ServiceConfigurationError("No SPI implementation for: " + key));
            try {
                return implClass.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                throw new ServiceConfigurationError("Failed to instantiate: " + implClass.getName(), e);
            }
        });
    }

    /**
     * 按需加载
     * @param serviceClass
     */
    private static synchronized void loadIfNeeded(Class<?> serviceClass) {
        if (!SERVICE_REGISTRY.containsKey(serviceClass)) {
            load(serviceClass);
        }
    }

    /**
     * 配置加载
     * @param serviceClass
     */
    private static void load(Class<?> serviceClass) {
        Map<String, Class<?>> implMap = new LinkedHashMap<>();
        Arrays.stream(SPI_PATHS)
                .flatMap(path -> {
                    try {
                        // 使用类加载器加载所有资源
                        Enumeration<URL> resources = Thread.currentThread()
                                .getContextClassLoader()
                                .getResources(path + serviceClass.getName());
                        List<URL> urls = new ArrayList<>();
                        while (resources.hasMoreElements()) {
                            urls.add(resources.nextElement());
                        }
                        return urls.stream();
                    } catch (IOException e) {
                        throw new RuntimeException("Failed to load SPI resources", e);
                    }
                })
                .sorted(Comparator.comparing(URL::toString)) // 确保自定义配置覆盖系统配置
                .forEach(url -> parseConfigFile(serviceClass, url, implMap));
        log.info("serviceClass : {}",serviceClass.getName());
        log.info("implMap : {}", implMap.toString());

        SERVICE_REGISTRY.put(serviceClass, implMap);
        log.info("SERVICE_REGISTRY : {} ",SERVICE_REGISTRY.toString());
    }

    /**
     * 配置文件解析
     * @param serviceClass
     * @param url
     * @param implMap
     */
    private static void parseConfigFile(Class<?> serviceClass, URL url, Map<String, Class<?>> implMap) {
        // 扫描路径，用户自定义的 SPI 优先级高于系统 SPI
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
            reader.lines()
                    .filter(line -> !line.trim().isEmpty())
                    .forEach(line -> {
                        String[] strArray = line.split("=");
                        if (strArray.length > 1) {
                            String key = strArray[0];
                            String className = strArray[1];
                            try {
                                Class<?> clazz = Class.forName(className);
                                if (!serviceClass.isAssignableFrom(clazz)) {
                                    throw new ServiceConfigurationError(className + " not subtype of " + serviceClass.getName());
                                }
                                implMap.put(key, clazz);
                            } catch (ClassNotFoundException e) {
                                log.error("SPI class not found: {}", className);
                            }
                        } else {
                            log.error("Invalid config line: {}", line);
                        }
                    });
        } catch (IOException e) {
            log.error("Error loading SPI configuration: {}", url, e);
        }
    }



    private static String getCacheKey(Class<?> serviceClass, String key) {
        return serviceClass.getName() + "#" + key;
    }
}